/*
 * \brief   Pistachio protection-domain facility
 * \author  Christian Helmuth
 * \date    2006-04-11
 */

/*
 * Copyright (C) 2006-2009 Christian Helmuth
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _CORE__INCLUDE__PLATFORM_PD_H_
#define _CORE__INCLUDE__PLATFORM_PD_H_

#include <platform_thread.h>

namespace Pistachio {
#include <l4/types.h>
}

namespace Genode {

	class Platform_thread;
	class Platform_pd
	{
		private:

			friend class Platform_thread;

			/*
			 * L4 thread ID has 18 bits for thread number and 14 bits for
			 * version info.
			 */
			enum {
				TASK_BITS    = 9,
				THREAD_BITS  = 9,
				VERSION_BITS = 14,
				TASK_MAX     = (1 << TASK_BITS),
				THREAD_MAX   = (1 << THREAD_BITS),
				VERSION_MAX  = (1 << VERSION_BITS),
				TASK_INVALID = -1,
			};

			unsigned _pd_id;    /* plain pd number */
			unsigned _version;  /* version number */

			Pistachio::L4_ThreadId_t _l4_task_id;  /* L4 task ID */

			/**
			 * Manually construct L4 thread ID from its components
			 */
			Pistachio::L4_ThreadId_t make_l4_id(unsigned taskno,
			                                    unsigned threadno,
			                                    unsigned version)
			{
				return Pistachio::L4_GlobalId((taskno << THREAD_BITS) | threadno, version);
			}


			/**********************************************
			 ** Threads of this protection domain object **
			 **********************************************/

			Platform_thread *_threads[THREAD_MAX];

			/**
			 * Initialize thread allocator
			 */
			void _init_threads();

			/**
			 * Thread iteration for one task
			 */
			Platform_thread *_next_thread();

			/**
			 * Thread allocation
			 *
			 * Again a special case for Core thread0.
			 */
			int _alloc_thread(int thread_id, Platform_thread *thread);

			/**
			 * Thread deallocation
			 *
			 * No special case for Core thread0 here - we just never call it.
			 */
			void _free_thread(int thread_id);


			/****************************
			 ** Pd (L4 task) allocator **
			 ****************************/

			struct Pd_alloc
			{
				unsigned reserved : 1;
				unsigned free     : 1;
				unsigned version  : VERSION_BITS;

				Pd_alloc(bool r, bool f, unsigned v)
				: reserved(r), free(f), version(v) { }

				/*
				 * Start with version 2 to avoid being mistaken as local or
				 * interrupt thread ID.
				 */
				Pd_alloc() : reserved(0), free(0), version(2) { }
			};

			static Pd_alloc _tasks[TASK_MAX];

			Pistachio::L4_Word_t _kip_ptr;
			Pistachio::L4_Word_t _utcb_ptr;

			/**
			 * Protection-domain creation
			 *
			 * The syscall parameter propagates if any L4 kernel function
			 * should be used. We need the special case for the Core startup.
			 */
			void _create_pd(bool syscall);

			/**
			 * Protection domain destruction
			 *
			 * No special case for Core here - we just never call it.
			 */
			void _destroy_pd();

			/**
			 * Protection domain allocation
			 *
			 * Find free L4 task and use it. We need the special case for Core
			 * startup.
			 */
			int _alloc_pd(signed task_id);

			/**
			 * Protection domain deallocation
			 *
			 * No special case for Core here - we just never call it.
			 */
			void _free_pd();

			/**
			 * Setup KIP and UTCB area
			 */
			void _setup_address_space();

			/**
			 * Return the location of the UTCB for the specified thread
			 */
			Pistachio::L4_Word_t _utcb_location(unsigned int thread_id);


			/***************
			 ** Debugging **
			 ***************/

			void _debug_log_pds(void);
			void _debug_log_threads(void);

		public:
			/**
			 * Constructors
			 */
			Platform_pd(bool core);
			Platform_pd(signed task_id = TASK_INVALID, bool create = true);

			/**
			 * Destructor
			 */
			~Platform_pd();

			static Pistachio::L4_Word_t _core_utcb_ptr;
			static void touch_utcb_space();

			/**
			 * Initialize L4 task facility
			 */
//			static void init(Allocator_avl *ram_alloc);

			/**
			 * Bind thread to task
			 *
			 * \return  0  on success or
			 *         -1  if thread ID allocation failed.
			 *
			 * This function allocates the physical L4 thread ID.
			 */
			int bind_thread(Platform_thread *thread);
			int bind_initial_thread(Platform_thread *thread);

			/**
			 * Unbind thread from protection domain
			 *
			 * Free the thread's slot and update thread object.
			 */
			void unbind_thread(Platform_thread *thread);

			int pd_id() const { return _pd_id; }
	};
}

#endif /* _CORE__INCLUDE__PLATFORM_PD_H_ */
